package com.yunli.mall.common.handler;

import com.yunli.mall.common.exception.MallRuntimeException;
import com.yunli.mall.common.exception.ErrorCode;
import com.yunli.mall.common.schema.ResponseInvoke;
import com.yunli.mall.common.util.IdGenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.sql.SQLException;

/**
 * @author wangyue
 * @description
 * @date 2019-11-15 15:59
 */
@Slf4j
@RestControllerAdvice
public class ExceptionHandlerAdvice {

    /**
     * 自定义异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MallRuntimeException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResponseInvoke processCustomException(MallRuntimeException e) {
        String errorCode = IdGenUtil.nextSimpleId();
        log.error("MallRuntimeException:{}", errorCode, e);
        if (e.getCode().equals(ErrorCode.INTERNAL_ERROR_HIDE_MSG.getCode())) {
            e.setMessage(ErrorCode.INTERNAL_ERROR.getMessage() + ",错误号:" + errorCode);
        }
        return ResponseInvoke.error(e);
    }

    /**
     * 参数校验异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ServletRequestBindingException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseInvoke processCustomException(ServletRequestBindingException e) {
        log.error("ServletRequestBindingException:", e);
        return ResponseInvoke.error(ErrorCode.PARAM_ERROR, ErrorCode.PARAM_ERROR.getMessage(), e.getMessage());
    }

    /**
     * 参数校验异常
     *
     * @param e Exception
     * @return Object
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseInvoke processValidateException(MethodArgumentNotValidException e) {
        StringBuilder errorMsg = new StringBuilder();
        e.getBindingResult().getFieldErrors().forEach(fieldError -> errorMsg.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(";"));
        log.error("MethodArgumentNotValidException:{}", errorMsg.toString(), e);
        return ResponseInvoke.error(ErrorCode.PARAM_ERROR, ErrorCode.PARAM_ERROR.getMessage(), errorMsg.toString());
    }

    /**
     * 参数校验异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseInvoke processBindException(BindException e) {
        StringBuilder errorMsg = new StringBuilder();
        e.getBindingResult().getFieldErrors().forEach(fieldError -> errorMsg.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(";"));
        log.error("BindException:{}", errorMsg.toString(), e);
        return ResponseInvoke.error(ErrorCode.PARAM_ERROR, ErrorCode.PARAM_ERROR.getMessage(), errorMsg.toString());
    }

    /**
     * 参数校验异常
     * 拦截Assert抛出的错
     * 但是NumberFormatException也会被拦截,所以判断处理下
     *
     * @param e
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseInvoke processBindException(IllegalArgumentException e) {
        if (e.getClass().equals(IllegalArgumentException.class)) {
            log.error("IllegalArgumentException:", e);
            return ResponseInvoke.error(ErrorCode.PARAM_ERROR, ErrorCode.PARAM_ERROR.getMessage(), e.getMessage());
        } else {
            String errorCode = IdGenUtil.nextSimpleId();
            log.error("IllegalArgumentException: {}", errorCode, e);
            return ResponseInvoke.error(ErrorCode.INTERNAL_ERROR, ErrorCode.INTERNAL_ERROR.getMessage(), "错误号:" + errorCode);
        }
    }

    /**
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseInvoke httpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("HttpMessageNotReadableException:", e);
        return ResponseInvoke.error(ErrorCode.PARAM_ERROR, ErrorCode.PARAM_ERROR.getMessage());
    }


    /**
     * @param e
     * @return
     */
    @ExceptionHandler(SQLException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseInvoke sqlexception(SQLException e) {
        String errorCode = IdGenUtil.nextSimpleId();
        log.error("SQLException {}", errorCode, e);
        return ResponseInvoke.error(ErrorCode.INTERNAL_ERROR, "数据库访问出错，错误号：" + errorCode);
    }

    /**
     * @param e
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseInvoke httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return ResponseInvoke.error(ErrorCode.INTERNAL_ERROR, "不支持的请求方式");
    }

    /**
     * @param e
     * @return
     */
    @ExceptionHandler(ConnectException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseInvoke connectExceptionException(ConnectException e) {
        String errorCode = IdGenUtil.nextSimpleId();
        log.error("ConnectException:{}", errorCode, e);
        return ResponseInvoke.error(ErrorCode.INTERNAL_ERROR, "服务不可用，错误号：" + errorCode);
    }

    @ExceptionHandler(SocketTimeoutException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseInvoke socketTimeoutException(SocketTimeoutException e) {
        log.error("SocketTimeoutException", e);
        return ResponseInvoke.error(ErrorCode.SERVICE_UNAVAILABLE);
    }

    /**
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseInvoke exception(Exception e) {
        // feign.RetryableException
        if (null != e.getCause() && SocketTimeoutException.class.equals(e.getCause().getClass())) {
            log.error("Exception-SocketTimeoutException", e);
            return ResponseInvoke.error(ErrorCode.SERVICE_UNAVAILABLE);
        }
        String errorCode = IdGenUtil.nextSimpleId();
        log.error("Exception: {}", errorCode, e);
        return ResponseInvoke.error(ErrorCode.INTERNAL_ERROR, ErrorCode.INTERNAL_ERROR.getMessage(), "错误号:" + errorCode);
    }

}
